package org.tangerinejunction.imstats.logs;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Calendar;

import org.tangerinejunction.imstats.data.Account;
import org.tangerinejunction.imstats.data.Entity;
import org.tangerinejunction.imstats.data.IMDataset;

import plistreader.AbstractReader;
import plistreader.PlistProperties;
import plistreader.PlistReaderException;

/**
 * This is an instant messaging log file reader specifically for Adium,
 * an instant messaging client based on pidgin and available for Mac OS X.
 * 
 * @author meganthorsen
 */
public class AdiumLogReader 
{
	public static final char PROTOCOL_SEPARATOR = '.';
	public static final char OLD_DATE_SEPARATOR = '|';
	public static final char NEW_DATE_SEPARATOR = '-';
	public static final char DATE_TIME_SEPARATOR = 'T'; 
	
	public static final String XML_LOG_SUFFIX = ".xml";
	public static final String HTML_LOG_SUFFIX = ".AdiumHTMLLog";
	public static final String HTML_SUFFIX = ".AdiumHTMLLog";
	public static final String CHAT_LOG_SUFFIX = ".chatlog";
	
	public static final String ALIAS_PROPERTY = "Alias";
	
	/**
	 * 
	 * @param baseFolder	the folder at the base of the logs of interest
	 * @param objectFolder	the ByObject folder associated with Adium
	 * @param imDataset		the dataset into which we want to read the logs
	 */
	public static void readLogs(File baseFolder, File objectFolder, IMDataset imDataset)
	{
		assert baseFolder.isDirectory() : "Error: base folder must be a directory";
		File[] myAccountFolders = baseFolder.listFiles();
		for (File myAccountFolder: myAccountFolders)
			if (myAccountFolder.isDirectory())
				readAccountLogs(objectFolder, myAccountFolder, imDataset);
	}
	
	/**
	 * 
	 * @param baseFolder	the highest level folder from which we're reading the logs
	 * @param objectFolder	the Adium ByObject folder
	 * @param imDataset		the dataset into which we're reading logs
	 * @param lastRead		the Calendar at which we last read the logs
	 */
	public static void updateLogs(File baseFolder, File objectFolder, IMDataset imDataset, Calendar lastRead)
	{
		assert baseFolder.isDirectory() : "Error: base folder must be a directory";
		File[] myAccountFolders = baseFolder.listFiles();
		for (File myAccountFolder: myAccountFolders)
			if (myAccountFolder.isDirectory())
				updateAccountLogs(objectFolder, myAccountFolder, imDataset, lastRead);
	}
	
	/**
	 * 
	 * @param objectFolder		the Adium ByObject folder
	 * @param myAccountFolder	a log folder associated with a specific user account
	 * @param imDataset			the dataset into which we're reading logs
	 */
	public static void readAccountLogs(File objectFolder, File myAccountFolder, IMDataset imDataset)
	{
		assert myAccountFolder.isDirectory() : "Error: personal account folder must be a directory";
		int protocolSeparatorIndex = myAccountFolder.getName().indexOf(PROTOCOL_SEPARATOR);
		String protocol = myAccountFolder.getName().substring(0, protocolSeparatorIndex);
		String myAccountName = myAccountFolder.getName().substring(protocolSeparatorIndex+1, 
				myAccountFolder.getName().length());
		File[] accountFolders = myAccountFolder.listFiles();
		for (File accountFolder: accountFolders)
			if (accountFolder.isDirectory())
				readAccountLogs(objectFolder, accountFolder, protocol, myAccountName, imDataset);
	}
	
	/**
	 * 
	 * @param objectFolder		the Adium ByObject folder
	 * @param myAccountFolder	a log folder associated with a specific user account
	 * @param imDataset			the dataset into which we're reading logs
	 * @param lastRead			the Calendar at which we last read the logs
	 */
	public static void updateAccountLogs(File objectFolder, File myAccountFolder, IMDataset imDataset, Calendar lastRead)
	{
		assert myAccountFolder.isDirectory() : "Error: personal account folder must be a directory";
		int protocolSeparatorIndex = myAccountFolder.getName().indexOf(PROTOCOL_SEPARATOR);
		String protocol = myAccountFolder.getName().substring(0, protocolSeparatorIndex);
		String myAccountName = myAccountFolder.getName().substring(protocolSeparatorIndex+1, 
				myAccountFolder.getName().length());
		File[] accountFolders = myAccountFolder.listFiles();
		for (File accountFolder: accountFolders)
			if (accountFolder.isDirectory())
				updateAccountLogs(objectFolder, accountFolder, protocol, myAccountName, imDataset, lastRead);
	}
	
	/**
	 * 
	 * @param objectFolder		the Adium ByObject folder
	 * @param accountFolder		a folder associated with a specific conversation partner
	 * @param protocol			the name of the instant messaging protocol used
	 * @param myAccountName		the name of the user's account used for chatting with this conversation partner
	 * @param imDataset			the dataset into which we're reading the logs
	 */
	public static void readAccountLogs(File objectFolder, File accountFolder, String protocol, String myAccountName, IMDataset imDataset)
	{
		assert accountFolder.isDirectory() : "Error: account folder must be a directory";
		String accountName = accountFolder.getName();
		int row = imDataset.getAccountTable().addRow();
		//Account account = new Account(protocol, myAccountName, accountName, imDataset.getAccountTable(), imDataset.getGraph(), row);
		Account account = new Account();
		Entity dummy = new Entity();
		int entityRow = imDataset.getEntityTable().addRow();
		dummy.init(imDataset.getEntityTable(), imDataset.getGraph(), entityRow, getAccountAlias(objectFolder, protocol, accountName));
		account.init(protocol, myAccountName, accountName, imDataset.getAccountTable(), imDataset.getGraph(), row, dummy);
		imDataset.addAccountToEntityByName(dummy.getName(), account);
		//dummy.addAccount(account);
		//imDataset.addAccount(account);
		File[] chatFiles = accountFolder.listFiles();
		for (File chatFile: chatFiles)
			if (isChatFile(chatFile)) 
				readChatFile(chatFile, account, imDataset);
			else
				System.err.println("file not read: " + chatFile.getAbsolutePath());
	}
	
	/**
	 * 
	 * @param objectFolder	the Adium ByObject folder
	 * @param accountFolder	a folder associated with a specific conversation partner
	 * @param protocol		the instant messaging protocol used
	 * @param myAccountName	the name of the user's account used to chat with this conversation partner
	 * @param imDataset		the dataset into which to read the logs
	 * @param lastRead		the last Calendar at which we read the logs
	 */
	public static void updateAccountLogs(File objectFolder, File accountFolder, String protocol, String myAccountName, IMDataset imDataset, Calendar lastRead)
	{
		assert accountFolder.isDirectory() : "Error: account folder must be a directory";
		String accountName = accountFolder.getName();
		Account account = imDataset.getAccountByName(accountName);
		if (account == null)
		{
			int row = imDataset.getAccountTable().addRow();
			account = new Account();
			Entity dummy = new Entity();
			int entityRow = imDataset.getEntityTable().addRow();
			dummy.init(imDataset.getEntityTable(), imDataset.getGraph(), entityRow, getAccountAlias(objectFolder, protocol, accountName));
			account.init(protocol, myAccountName, accountName, imDataset.getAccountTable(), imDataset.getGraph(), row, dummy);
			imDataset.addAccountToEntityByName(dummy.getName(), account);
		}
		File[] chatFiles = accountFolder.listFiles();
		for (File chatFile: chatFiles)
		{
			Calendar lastMod = Calendar.getInstance();
			lastMod.setTimeInMillis(chatFile.lastModified());
			Calendar created = Calendar.getInstance();
			if (isChatFile(chatFile) && lastMod.after(lastRead))
				readChatFile(chatFile, account, imDataset);
			//else
			//	System.err.println("file not read for update: " + chatFile.getAbsolutePath());
		}
	}
	
	/**
	 * 
	 * @param chatFile	a specific chat log file to read
	 * @param account	the Account with which to associate the file
	 * @param imDataset	the dataset into which we're reading logs
	 */
	public static void readChatFile(File chatFile, Account account, IMDataset imDataset)
	{
		if (chatFile.isDirectory())
		{
			File[] files = chatFile.listFiles();
			for (int i = 0; i < files.length; i++)
				if (isChatFile(files[i]))
					readChatFile(files[i], account, imDataset);
			return;
		}
		Calendar logDate = readDate(chatFile.getName());
		LogFile logFile = new LogFile(chatFile, account, logDate);
		//account.addLogFile(logFile);
		imDataset.addLogFile(logFile);
	}
	
	/**
	 * 
	 * @param objectFolder	the Adium ByObject folder
	 * @param protocol		the name of the instant messaging protocol used
	 * @param accountName	the name of the conversation partner's account
	 * @return
	 */
	public static String getAccountAlias(File objectFolder, String protocol, String accountName)
	{
		File entityFile = new File(EntityWriter.ENTITY_FILE_NAME);
		if (entityFile.exists())
		{
			try 
			{
				BufferedReader reader = new BufferedReader(new FileReader(entityFile));
				String line = reader.readLine();
				while (line != null)
				{
					String[] tokens = line.split(EntityWriter.SPLITTER);
					if (tokens[0].equalsIgnoreCase(accountName))
						return tokens[1];
					line = reader.readLine();
				}
			} 
			catch (FileNotFoundException e) 
			{
				e.printStackTrace();
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
		}
		
		/*
		File accountPlist = new File(objectFolder, protocol + "." + accountName + ".plist");
		if (accountPlist.exists())
		{
			AbstractReader reader = new AbstractReader(accountPlist)
			{
				
			};
			try
			{
				PlistProperties props = reader.parse(accountPlist);
				return (String)props.getProperty(ALIAS_PROPERTY);
			}
			catch (PlistReaderException e)
			{
				e.printStackTrace();
				return accountName;
			}
		}
		*/
		return accountName;
	}
	
	/**
	 * 
	 * @param file	a file
	 * @return		whether this file is a chat log
	 */
	public static boolean isChatFile(File file)
	{
		if (file.getName().endsWith(HTML_LOG_SUFFIX) 
				|| file.getName().endsWith(HTML_SUFFIX)
				|| file.getName().endsWith(CHAT_LOG_SUFFIX)
				|| file.getName().endsWith(XML_LOG_SUFFIX)) 
			return true;
		else
			return false;
	}
	
	/**
	 * 
	 * @param fileName	the name of a file
	 * @return			the Calendar date associated with the file
	 */
	private static Calendar readDate(String fileName)
	{
		int openParenIndex = fileName.indexOf('(');
		int closeParenIndex = fileName.indexOf(')');
		String dateString = fileName.substring(openParenIndex+1, closeParenIndex);
		Calendar logDate = Calendar.getInstance();
		if (fileName.endsWith(HTML_LOG_SUFFIX) 
				|| fileName.endsWith(HTML_SUFFIX))
		{
			if (dateString.indexOf(OLD_DATE_SEPARATOR) >= 0)
			{
				// TODO: figure out why this didn't work
				//String[] dateItems = dateString.split(Character.toString(OLD_DATE_SEPARATOR));
				int year = Integer.parseInt(dateString.substring(0, 4));
				int month = Integer.parseInt(dateString.substring(5, 7));
				int day = Integer.parseInt(dateString.substring(8, 10));
				logDate.set(year, month-1, day, 0, 0, 0);
				return logDate;
			}
			String[] dateItems = dateString.split(Character.toString(NEW_DATE_SEPARATOR));
			logDate.set(Integer.parseInt(dateItems[0]), Integer.parseInt(dateItems[1])-1, 
					Integer.parseInt(dateItems[2]));
			return logDate;
		}
		else if (fileName.endsWith(CHAT_LOG_SUFFIX) 
				|| fileName.endsWith(XML_LOG_SUFFIX))
		{
			String[] dateItems = dateString.split(Character.toString(NEW_DATE_SEPARATOR));
			int year = Integer.parseInt(dateItems[0]);
			int month = Integer.parseInt(dateItems[1]);
			int day = Integer.parseInt(dateItems[2].substring(0, 2));
			String timeString = dateString.substring(dateString.indexOf(DATE_TIME_SEPARATOR)+1, dateString.length()-1);
			int hour = Integer.parseInt(timeString.substring(0, 2));
			int minute = Integer.parseInt(timeString.substring(3,5));
			int second = Integer.parseInt(timeString.substring(6,8));
			logDate.set(year, month-1, day, hour, minute, second);
			// TODO: time zone stuff
			//TimeZone timeZone = TimeZone.getTimeZone();
			return logDate;
		}
		else
		    System.err.println("readDate - FILE NOT CORRECT FORMAT: " + fileName);
		// TODO: throw exception
		return logDate;
	}
}
